package edu.mapi.aamas.common;

import java.util.LinkedHashMap;
import java.util.Vector;

/**
 * @author ZP
 */
public class WorldState {
	private LinkedHashMap<String, VehicleState> vehicles = new LinkedHashMap<String, VehicleState>();
	private LinkedHashMap<String, Long> timestamps = new LinkedHashMap<String, Long>();
	
	private LinkedHashMap<String, Task> tasks = new LinkedHashMap<String, Task>();
	private LinkedHashMap<String, String> vehicleAssociations = new LinkedHashMap<String, String>();
	
	/**
	 * Given a vehicle ID, returns its associated state
	 * @param vehicleId The vehicle's id
	 */
	public VehicleState getVehicleState(String vehicleId) {
		synchronized (timestamps) {
			return vehicles.get(vehicleId);
		}
	}
	
	public String[] getKnownVehicleIds() {
		return vehicles.keySet().toArray(new String[0]);
	}
	
	public VehicleState[] getVehicleStates() {
		return vehicles.values().toArray(new VehicleState[0]);
	}
	
	/**
	 * Set the state associated with a vehicle
	 * @param vehicleId The vehicle's id
	 * @param state The current associated state
	 * @return The previous associated state
	 */
	public VehicleState putVehicleState(String vehicleId, VehicleState state) {
		synchronized (timestamps) {
			timestamps.put(vehicleId, System.currentTimeMillis());
		}
		return vehicles.put(vehicleId, state);		
	}
	
	/**
	 * Given a maximum idle time, every vehicle state which timestamp is too old is removed from the world state
	 * @param maximumIdleTimeMillis The maximum idle time, in milliseconds
	 */
	public void removeOldEntries(long maximumIdleTimeMillis) {
		
		Vector<String> remove_ids = new Vector<String>();
		
		long curTime = System.currentTimeMillis();
		
		for (String s : timestamps.keySet())
			if (curTime - timestamps.get(s) > maximumIdleTimeMillis) 
				remove_ids.add(s);
		
		synchronized (timestamps) {
			for (String id : remove_ids) {							
				timestamps.remove(id);
				vehicles.remove(id);					
			}
		}
	}
	
	/**
	 * Add a new task for execution
	 * @param taskId The task identifier
	 * @param task The task object/description
	 */
	public void addTask(String taskId, Task task) {
		tasks.put(taskId, task);		
	}

	/**
	 * Associate a task to a given vehicle
	 * @param taskId The id of the task to be associated
	 * @param vehicleId The vehicle's identifier
	 */
	void setTaskAssociation(String taskId, String vehicleId) {
		vehicleAssociations.put(taskId, vehicleId);
	}
	
	/**
	 * Remove a (completed) task from the world state
	 * @param taskId The task to be removed
	 */
	public void removeTask(String taskId) {
		tasks.remove(taskId);
	}
	
	/**
	 * Given a task id, returns its associated vehicle or <b>null</b> if no vehicle is already associated
	 * @param taskId the task id
	 * @return The associated vehicle's id.
	 */
	public String getAssociatedVehicle(String taskId) {
		return vehicleAssociations.get(taskId);
	}
	
	/**
	 * @return All tasks that aren't currently associated with any vehicle
	 */
	public Vector<Task> getUnassociatedTasks() {
		Vector<Task> unassociatedTasks = new Vector<Task>();
		for (String taskId : vehicleAssociations.keySet()) {
			if (vehicleAssociations.get(taskId) == null)
				unassociatedTasks.add(tasks.get(taskId));
		}
		return unassociatedTasks;
	}
}
